package temporay;

import temporay.codec.Codec;
import temporay.message.Request;
import temporay.message.Response;
import temporay.util.ClassUtil;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * program: io-share 
 * author: bsworld.xie 
 * create: 2021-04-20 15:18 
 * description: 
 */
public class Connection {
    private ByteBuffer incomingBuffer = null;

    private final Queue<ByteBuffer> outgoingBuffers = new LinkedBlockingQueue<ByteBuffer>();

    private SocketChannel socketChannel;

    private SelectionKey sk;

    private SelectorThread selectorThread;


    public Connection(SocketChannel socketChannel, SelectionKey selectionKey, SelectorThread selectorThread) {
        this.socketChannel = socketChannel;
        this.sk = selectionKey;
        this.selectorThread = selectorThread;
    }

    public void sendResponse(Response response) {
        ByteBuffer encode = Codec.encode(response);
        sendBuffer(encode);
    }

    void sendBuffer(ByteBuffer... buffers) {
        synchronized (outgoingBuffers) {
            for (ByteBuffer buffer : buffers) {
                outgoingBuffers.add(buffer);
            }
        }
        requestInterestOpsUpdate();
    }


    private void requestInterestOpsUpdate() {
        selectorThread.addInterestOpsUpdateRequest(sk);
    }


    public int getInterestOps() {
        int interestOps = 0 | SelectionKey.OP_READ;
        if (getWriteInterest()) {
            interestOps |= SelectionKey.OP_WRITE;
        }
        return interestOps;
    }

    private boolean getWriteInterest() {
        return !outgoingBuffers.isEmpty();
    }

    /**
     * Handles read/write IO on connection.
     */
    public void doIO(SelectionKey k) {
        try {

            if (k.isReadable()) {
                //incomingBuffer从读取数据进行业务处理
                socketChannel.read(incomingBuffer);
                Request request = Codec.deCode(incomingBuffer);

                Method method = ClassUtil.findProxyClazzMethod(request.getClazzName(), request.getMethod());
                Object proxyObject = ClassUtil.findProxyClazzMethod(request.getClazzName(), request.getMethod());
                method.invoke(proxyObject, request);

            }

            if (k.isWritable()) {
                //向outgoingBuffers里添加数据
                if (!outgoingBuffers.isEmpty()) {
                    return;
                }
                //fixme 编码发送数据  伪代码
                for (ByteBuffer outgoingBuffer : outgoingBuffers) {
                    socketChannel.write(outgoingBuffer);
                }
            }
        } catch (CancelledKeyException | IOException e) {

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}
